import { isArray } from '@/utils/is'

/**
 * @description 获取localStorage
 * @param {String} key Storage名称
 * @return string
 */
export function localGet(key: string) {
	const value = window.localStorage.getItem(key)
	try {
		return JSON.parse(window.localStorage.getItem(key) as string)
	} catch (error) {
		return value
	}
}

/**
 * @description 存储localStorage
 * @param {String} key Storage名称
 * @param {Any} value Storage值
 * @return void
 */
export function localSet(key: string, value: any) {
	window.localStorage.setItem(key, JSON.stringify(value))
}

/**
 * @description 清除localStorage
 * @param {String} key Storage名称
 * @return void
 */
export function localRemove(key: string) {
	window.localStorage.removeItem(key)
}

/**
 * @description 清除所有localStorage
 * @return void
 */
export function localClear() {
	window.localStorage.clear()
}

/**
 * 生成uuid
 * @returns 三十二位唯一值
 */
export function uuid() {
	const timestamp = +new Date() + ''
	const randomNum = parseInt((1 + Math.random()) * 65536 + '') + ''
	return (+(randomNum + timestamp)).toString(32)
}

/**
 * @description 判断数据类型
 * @param {Any} val 需要判断类型的数据
 * @return string
 */
export function isType(val: any) {
	if (val === null) return 'null'
	if (typeof val !== 'object') return typeof val
	else return Object.prototype.toString.call(val).slice(8, -1).toLocaleLowerCase()
}

/**
 * 判断两个对象是否相同
 * @param a 要比较的对象一
 * @param b 要比较的对象二
 * @returns 相同返回 true，反之则反
 */
export function isObjectValueEqual(a: { [key: string]: any }, b: { [key: string]: any }) {
	if (!a || !b) return false
	let aProps = Object.getOwnPropertyNames(a)
	let bProps = Object.getOwnPropertyNames(b)
	if (aProps.length != bProps.length) return false
	for (let i = 0; i < aProps.length; i++) {
		let propName = aProps[i]
		let propA = a[propName]
		let propB = b[propName]
		if (!b.hasOwnProperty(propName)) return false
		if (propA instanceof Object) {
			if (!isObjectValueEqual(propA, propB)) return false
		} else if (propA !== propB) {
			return false
		}
	}
	return true
}

/**
 * @description 生成随机数
 * @param {Number} min 最小值
 * @param {Number} max 最大值
 * @return number
 */
export function randomNum(min: number, max: number): number {
	let num = Math.floor(Math.random() * (min - max) + max)
	return num
}

/**
 * @description 获取当前时间
 * @return string
 */
export function getTimeState() {
	// 获取当前时间
	let timeNow = new Date()
	// 获取当前小时
	let hours = timeNow.getHours()
	// 判断当前时间段
	if (hours >= 6 && hours <= 10) return `早上好 ⛅`
	if (hours >= 10 && hours <= 14) return `中午好 🌞`
	if (hours >= 14 && hours <= 18) return `下午好 🌞`
	if (hours >= 18 && hours <= 24) return `晚上好 🌛`
	if (hours >= 0 && hours <= 6) return `凌晨好 🌛`
}

/**
 * @description 获取浏览器默认语言
 * @return string
 */
export function getBrowserLang() {
	let browserLang = navigator.language ? navigator.language : navigator.browserLanguage
	let defaultBrowserLang = ''
	if (browserLang.toLowerCase() === 'cn' || browserLang.toLowerCase() === 'zh' || browserLang.toLowerCase() === 'zh-cn') {
		defaultBrowserLang = 'zh'
	} else {
		defaultBrowserLang = 'en'
	}
	return defaultBrowserLang
}

/**
 * @description 递归查询当前路由所对应的路由
 * @param {Array} menuList 所有菜单列表
 * @param {String} path 当前访问地址
 * @return array
 */
export function filterCurrentRoute(menuList: Menu.MenuOptions[], path: string) {
	let result = {}
	for (let item of menuList) {
		if (item.path === path) return item
		if (item.children) {
			const res = filterCurrentRoute(item.children, path)
			if (Object.keys(res).length) result = res
		}
	}
	return result
}

/**
 * @description 扁平化数组对象(主要用来处理路由菜单)
 * @param {Array} menuList 所有菜单列表
 * @return array
 */
export function getFlatArr(menulist: Menu.MenuOptions[]) {
	return menulist.reduce((pre: Menu.MenuOptions[], current: Menu.MenuOptions) => {
		let flatArr = [...pre, current]
		if (current.children) flatArr = [...flatArr, ...getFlatArr(current.children)]
		return flatArr
	}, [])
}

/**
 * @description 使用递归，过滤需要缓存的路由
 * @param {Array} menuList 所有菜单列表
 * @param {Array} cacheArr 缓存的路由菜单 name ['**','**']
 * @return array
 * */
export function getKeepAliveRouterName(menuList: Menu.MenuOptions[], keepAliveArr: string[] = []) {
	menuList.forEach(item => {
		item.meta.isKeepAlive && item.name && keepAliveArr.push(item.name)
		item.children?.length && getKeepAliveRouterName(item.children, keepAliveArr)
	})
	return keepAliveArr
}

/**
 * @description 使用递归，过滤出需要渲染在左侧菜单的列表（剔除 isHide == true 的菜单）
 * @param {Array} menuList 所有菜单列表
 * @return array
 * */
export function getShowMenuList(menuList: Menu.MenuOptions[]) {
	let newMenuList: Menu.MenuOptions[] = JSON.parse(JSON.stringify(menuList))
	return newMenuList.filter(item => {
		item.children?.length && (item.children = getShowMenuList(item.children))
		return !item.meta?.isHide
	})
}

/**
 * @description 使用递归处理路由菜单 path，生成一维数组(第一版本地路由鉴权会用到)
 * @param {Array} menuList 所有菜单列表
 * @param {Array} menuPathArr 菜单地址的一维数组 ['**','**']
 * @return array
 */
export function getMenuListPath(menuList: Menu.MenuOptions[], menuPathArr: string[] = []) {
	menuList.forEach((item: Menu.MenuOptions) => {
		typeof item === 'object' && item.path && menuPathArr.push(item.path)
		item.children?.length && getMenuListPath(item.children, menuPathArr)
	})
	return menuPathArr
}

/**
 * @description 使用递归，过滤出当前路径匹配的面包屑地址
 * @param {String} path 当前访问地址
 * @param {Array} menuList 所有菜单列表
 * @returns array
 */
export function getCurrentBreadcrumb(path: string, menuList: Menu.MenuOptions[]) {
	let tempPath: Menu.MenuOptions[] = []
	try {
		const getNodePath = (node: Menu.MenuOptions) => {
			tempPath.push(node)
			if (node.path === path) throw new Error('Find IT!')
			if (node.children?.length) node.children.forEach(item => getNodePath(item))
			tempPath.pop()
		}
		menuList.forEach(item => getNodePath(item))
	} catch (e) {
		return tempPath
	}
}

/**
 * @description 双重递归找出所有面包屑存储到 pinia/vuex 中
 * @param {Array} menuList 所有菜单列表
 * @returns array
 */
export function getAllBreadcrumbList(menuList: Menu.MenuOptions[]) {
	let handleBreadcrumbList: { [key: string]: any } = {}
	const loop = (menuItem: Menu.MenuOptions) => {
		if (menuItem?.children?.length) menuItem.children.forEach(item => loop(item))
		else handleBreadcrumbList[menuItem.path] = getCurrentBreadcrumb(menuItem.path, menuList)
	}
	menuList.forEach(item => loop(item))
	return handleBreadcrumbList
}

/**
 * @description 格式化表格单元格默认值(el-table-column)
 * @param {Number} row 行
 * @param {Number} col 列
 * @param {String} callValue 当前单元格值
 * @return string
 * */
export function defaultFormat(row: number, col: number, callValue: any) {
	// 如果当前值为数组,使用 / 拼接（根据需求自定义）
	if (isArray(callValue)) return callValue.length ? callValue.join(' / ') : '--'
	return callValue ?? '--'
}

/**
 * @description 处理无数据情况
 * @param {String} callValue 需要处理的值
 * @return string
 * */
export function formatValue(callValue: any) {
	// 如果当前值为数组,使用 / 拼接（根据需求自定义）
	if (isArray(callValue)) return callValue.length ? callValue.join(' / ') : '--'
	return callValue ?? '--'
}

/**
 * @description 根据枚举列表查询当需要的数据（如果指定了 label 和 value 的 key值，会自动识别格式化）
 * @param {String} callValue 当前单元格值
 * @param {Array} enumData 枚举列表
 * @param {String} type 过滤类型（目前只有 tag）
 * @return string
 * */
export function filterEnum(callValue: any, enumData: any, searchProps?: { [key: string]: any }, type?: string): string {
	const value = searchProps?.value ?? 'value'
	const label = searchProps?.label ?? 'label'
	let filterData: any = {}
	if (Array.isArray(enumData)) filterData = enumData.find((item: any) => item[value] === callValue)
	if (type == 'tag') return filterData?.tagType ? filterData.tagType : ''
	return filterData ? filterData[label] : '--'
}

/**
 * @description 根据字典列表查询当需要的数据
 * @param {String} callValue 当前单元格值
 * @param {Array} dictData 字典列表
 * @param {String} type 过滤类型（目前只有 tag）
 * @return string
 * */
export function filterDict(callValue: any, dictData: any, type?: string): string {
	let filterData: any = {}
	if (Array.isArray(dictData)) filterData = dictData.find((item: any) => item.value === callValue)
	if (type == 'type') return filterData?.type ? filterData.type : ''
	return filterData ? filterData.label : '--'
}

/**
 * @description  拼接图片
 * @param path
 * @returns 图片全路径
 */
export function getImagePath(path = ''): string {
	return '/src/assets' + import.meta.env.VITE_BASE_IMAGE_URL + path
}

/**
 * @description  list 转 tree
 * @param list
 * @param idName
 * @param pidName
 * @param childrenName
 * @param rootPid
 * @returns
 */
export function listToTree(
	list: { [propName: string]: any }[],
	idName: string = 'id',
	pidName: string = 'pid',
	childrenName: string = 'children',
	rootPid: string = ''
): { [propName: string]: any }[] {
	// 1.转map
	const map: { [key: string | number]: any } = {}
	list.forEach(item => {
		map[item[idName]] = item
	})
	// 2.关联
	for (const item of list) {
		const node = map[item[pidName]]
		if (!node) {
			continue
		}
		let children = node[childrenName]
		if (!children) {
			node[childrenName] = children = []
		}
		children.push(item)
	}
	// 3.获取根节点
	const result = map[rootPid]
	if (result) {
		return result
	}
	return list.filter(item => {
		return rootPid === item[pidName]
	})
}

/**
 * @description 浅拷贝属性，像在 dialog 的 open 事件中调用详情的时候怕有多余字段
 * @param source
 * @param taget
 * @returns
 */
export function copyProperties(source: { [key: string]: any }, taget: { [key: string]: any }) {
	if (!source || !taget) {
		return
	}
	for (const key in taget) {
		if (Object.hasOwnProperty.call(taget, key)) {
			if (source[key] !== undefined) {
				taget[key] = source[key]
			}
		}
	}
}

/**
 * @description 深度拷贝
 * @param source
 * @returns
 */
export function deepClone<T>(source: T): T {
	if (source && typeof source === 'object') {
		const targetObj = <any>(source.constructor === Array ? [] : {})
		for (const key in source) {
			if (Object.prototype.hasOwnProperty.call(source, key)) {
				targetObj[key] = deepClone(source[key])
			}
		}
		return targetObj
	} else if (source instanceof Date) {
		return new Date(source) as T
	} else {
		return source
	}
}
/**
 * @description 深度冻结对象
 * @param obj
 * @returns
 */
export function deepFreeze(obj: any): void {
	if (!obj && typeof obj !== 'object') {
		throw new Error('error arguments')
	}
	Object.freeze(obj)
	for (const key in obj) {
		if (Object.prototype.hasOwnProperty.call(obj, key)) {
			if (typeof obj[key] === 'object') {
				deepFreeze(obj[key])
			}
		}
	}
}

/**
 * 复制字符串
 * @param text 被复制的字符串
 */
export function copy(text: string) {
	const input = document.createElement('input')
	input.value = text
	document.body.appendChild(input)
	input.select()
	document.execCommand('Copy')
	document.body.removeChild(input)
}

/**
 * 获取本地图
 * @param name // 文件名 如 xx.png
 * @returns {*|string}
 */
export function getAssetsImages(name: string) {
	return new URL(`/src/assets/images/${name}`, import.meta.url).href
}

/**
 * 数字
 */
const REG_NUMBER: string = '.*\\d+.*'
/**
 * 小写字母
 */
const REG_UPPERCASE: string = '.*[A-Z]+.*'
/**
 * 大写字母
 */
const REG_LOWERCASE: string = '.*[a-z]+.*'
/**
 * 特殊符号(~!@#$%^&*()_+|<>,.?/:;'[]{}\)
 */
const REG_SYMBOL: string = '.*[~!@#$%^&*()_+|<>,.?/:;\'\\[\\]{}"]+.*'
/**
 * 键盘字符表(小写)
 * 非shift键盘字符表
 */
const CHAR_TABLE1: string[][] = [
	['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\0'],
	['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\\'],
	['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', "'", '\0', '\0'],
	['z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', '\0', '\0', '\0']
]
/**
 * shift键盘的字符表
 */
const CHAR_TABLE2: string[][] = [
	['!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\0'],
	['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '{', '}', '|'],
	['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ':', '"', '\0', '\0'],
	['z', 'x', 'c', 'v', 'b', 'n', 'm', '<', '>', '?', '\0', '\0', '\0']
]

/**
 * 校验密码是否符合条件
 * @param password 密码
 * @param username 用户名
 */
export const checkPasswordRule = (password: string, username: string) => {
	if (password === '' || password.length < 8 || password.length > 32) {
		// console.log("长度小于8，或大于32");
		return '密码长度应大于8小于32'
	}
	if (password.indexOf(username) !== -1) {
		// console.log("包含用户名");
		return '请勿包含用户名'
	}
	if (isContinuousChar(password)) {
		// console.log("包含3个及以上相同或字典连续字符");
		return '请勿包含3个及以上相同或连续的字符'
	}
	if (isKeyBoardContinuousChar(password)) {
		// console.log("包含3个及以上键盘连续字符");
		return '请勿包含3个及以上键盘连续字符'
	}
	let i: number = 0
	if (password.match(REG_NUMBER)) i++
	if (password.match(REG_LOWERCASE)) i++
	if (password.match(REG_UPPERCASE)) i++
	if (password.match(REG_SYMBOL)) i++
	if (i < 2) {
		// console.log(("数字、小写字母、大写字母、特殊字符，至少包含两种"));
		return '数字、小写字母、大写字母、特殊字符，至少包含两种'
	}
	// console.log(i);
	return 'success'
}

/**
 * 是否包含3个及以上相同或字典连续字符
 */
const isContinuousChar = (password: string) => {
	let chars: string[] = password.split('')
	let charCode: number[] = []
	for (let i = 0; i < chars.length - 2; i++) {
		charCode[i] = chars[i].charCodeAt(0)
	}
	for (let i = 0; i < charCode.length - 2; i++) {
		let n1 = charCode[i]
		let n2 = charCode[i + 1]
		let n3 = charCode[i + 2]
		// 判断重复字符
		if (n1 == n2 && n1 == n3) {
			return true
		}
		// 判断连续字符： 正序 + 倒序
		if ((n1 + 1 == n2 && n1 + 2 == n3) || (n1 - 1 == n2 && n1 - 2 == n3)) {
			return true
		}
	}
	return false
}
/**
 * 是否包含3个及以上键盘连续字符
 * @param password 待匹配的字符串
 */
const isKeyBoardContinuousChar = (password: string) => {
	if (password === '') {
		return false
	}
	//考虑大小写，都转换成小写字母
	let lpStrChars: string[] = password.toLowerCase().split('')
	// 获取字符串长度
	let nStrLen: number = lpStrChars.length
	// 定义位置数组：row - 行，col - column 列
	const pRowCharPos: number[] = new Array(nStrLen).fill('')
	const pColCharPos: number[] = new Array(nStrLen).fill('')
	for (let i = 0; i < nStrLen; i++) {
		let chLower: string = lpStrChars[i]
		pColCharPos[i] = -1
		// 检索在表1中的位置，构建位置数组
		for (let nRowTable1Idx = 0; nRowTable1Idx < 4; nRowTable1Idx++) {
			for (let nColTable1Idx = 0; nColTable1Idx < 13; nColTable1Idx++) {
				if (chLower == CHAR_TABLE1[nRowTable1Idx][nColTable1Idx]) {
					pRowCharPos[i] = nRowTable1Idx
					pColCharPos[i] = nColTable1Idx
				}
			}
		}
		// 在表1中没找到，到表二中去找，找到则continue
		if (pColCharPos[i] >= 0) {
			continue
		}
		// 检索在表2中的位置，构建位置数组
		for (let nRowTable2Idx = 0; nRowTable2Idx < 4; nRowTable2Idx++) {
			for (let nColTable2Idx = 0; nColTable2Idx < 13; nColTable2Idx++) {
				if (chLower == CHAR_TABLE2[nRowTable2Idx][nColTable2Idx]) {
					pRowCharPos[i] = nRowTable2Idx
					pColCharPos[i] = nColTable2Idx
				}
			}
		}
	}
	// 匹配坐标连线
	for (let j = 1; j <= nStrLen - 2; j++) {
		//同一行
		if (pRowCharPos[j - 1] == pRowCharPos[j] && pRowCharPos[j] == pRowCharPos[j + 1]) {
			// 键盘行正向连续（asd）或者键盘行反向连续（dsa）
			if (
				(pColCharPos[j - 1] + 1 == pColCharPos[j] && pColCharPos[j] + 1 == pColCharPos[j + 1]) ||
				(pColCharPos[j + 1] + 1 == pColCharPos[j] && pColCharPos[j] + 1 == pColCharPos[j - 1])
			) {
				return true
			}
		}
		//同一列
		if (pColCharPos[j - 1] == pColCharPos[j] && pColCharPos[j] == pColCharPos[j + 1]) {
			//键盘列连续（qaz）或者键盘列反向连续（zaq）
			if (
				(pRowCharPos[j - 1] + 1 == pRowCharPos[j] && pRowCharPos[j] + 1 == pRowCharPos[j + 1]) ||
				(pRowCharPos[j - 1] - 1 == pRowCharPos[j] && pRowCharPos[j] - 1 == pRowCharPos[j + 1])
			) {
				return true
			}
		}
	}
	return false
}

/**
 * 密码强度校验
 */
/**
 * 长度
 * @param str
 */
const length = (str: string) => {
	if (str.length < 5) {
		return 5
	} else if (str.length < 8) {
		return 10
	} else {
		return 25
	}
}
/**
 * 字母
 * @param str
 */
const letters = (str: string) => {
	let count1 = 0,
		count2 = 0
	for (let i = 0; i < str.length; i++) {
		if (str.charAt(i) >= 'a' && str.charAt(i) <= 'z') {
			count1++
		}
		if (str.charAt(i) >= 'A' && str.charAt(i) <= 'Z') {
			count2++
		}
	}
	if (count1 == 0 && count2 == 0) {
		return 0
	}
	if (count1 != 0 && count2 != 0) {
		return 20
	}
	return 10
}

/**
 * 数字
 * @param str
 */
const numbers = (str: string) => {
	let count = 0
	for (let i = 0; i < str.length; i++) {
		if (str.charAt(i) >= '0' && str.charAt(i) <= '9') {
			count++
		}
	}
	if (count == 0) {
		return 0
	}
	if (count == 1) {
		return 10
	}
	return 20
}
/**
 * 符号
 * @param str
 */
const symbols = (str: string) => {
	let count = 0
	for (let i = 0; i < str.length; i++) {
		if (
			(str.charCodeAt(i) >= 0x21 && str.charCodeAt(i) <= 0x2f) ||
			(str.charCodeAt(i) >= 0x3a && str.charCodeAt(i) <= 0x40) ||
			(str.charCodeAt(i) >= 0x5b && str.charCodeAt(i) <= 0x60) ||
			(str.charCodeAt(i) >= 0x7b && str.charCodeAt(i) <= 0x7e)
		) {
			count++
		}
	}
	if (count == 0) {
		return 0
	}
	if (count == 1) {
		return 10
	}
	return 25
}
/**
 * 得分机制
 * @param str
 */
const rewards = (str: string) => {
	let letter = letters(str) //字母
	let number = numbers(str) //数字
	let symbol = symbols(str) //符号
	if (letter > 0 && number > 0 && symbol == 0) {
		//字母和数字
		return 2
	}
	if (letter == 10 && number > 0 && symbol > 0) {
		//字母、数字和符号
		return 3
	}
	if (letter == 20 && number > 0 && symbol > 0) {
		//大小写字母、数字和符号
		return 5
	}
	return 0
}
/**
 * 最终评分
 * @param str
 */
export const level = (str: string) => {
	let lengths = length(str) //长度
	let letter = letters(str) //字母
	let number = numbers(str) //数字
	let symbol = symbols(str) //符号
	let reward = rewards(str) //奖励
	let sum = lengths + letter + number + symbol + reward
	console.log(sum)
	if (sum >= 80) {
		return 4 //非常安全
	} else if (sum >= 60) {
		return 3 //非常强
	} else if (sum >= 40) {
		return 2 //一般
	} else if (sum >= 25) {
		return 1 //弱
	} else {
		return 0 //非常弱
	}
}
